#include <memory.h>
#include "buf.h"
#include "db.h"

//----------------------------------------
// Constructor of the class BufMgr
//----------------------------------------

BufMgr::BufMgr(const unsigned int bufs)
{
    numBufs = bufs;
    clockHand = numBufs - 1;
    // initialze bufpool to buf pages
    bufPool = new Page[bufs];   
    // initialie buftable to contain bufdesc objects
    bufTable = new BufDesc[bufs];
}


BufMgr::~BufMgr() 
{
    //flush all dirty pages
    for (unsigned int i = 0; i < numBufs; i++)
    {
        if (bufTable[i].dirty)
        {
            bufTable[i].file->writePage(bufTable[i].pageNo, &bufPool[i]);
            bufStats.diskwrites++;
        }
    }
    delete [] bufPool;
    delete [] bufTable;
}


const Status BufMgr::allocBuf(unsigned int & frame) 
{ 
<<<<<<< .mine

    Status status = OK;

    if(numUnpinnedPages() == 0)
    {
        status = BUFFEREXCEEDED;
        return status;
    }

    while(1)
    {
        advanceClock();
 
	if(!bufTable[clockHand].valid)
	{
            break;
        }

	if(bufTable[clockHand].loved)
	{
	    bufTable[clockHand].loved = false;
	    continue;
	}
	
	if(bufTable[clockHand].pinCnt > 0)
	{
	    continue;
	}

	if(bufTable[clockHand].dirty)
	{
            status = bufTable[clockHand].file->writePage(bufTable[clockHand].pageNo, &bufPool[clockHand]);
            bufStats.diskwrites++;
        }
	
	break;

    }

    bufTable[clockHand].Clear();
    frame = clockHand;

    return status;
=======

    Status status = OK;

    //Return error if there are no unpinned pages available
    if(numUnpinnedPages() == 0)
    {
        status = BUFFEREXCEEDED;
        return status;
    }

    //clock algorithm to determine which buffer should be freed
    while(1)
    {
        advanceClock();
 
	if(!bufTable[clockHand].valid)
	{
            break;
        }
        //skip if loved
	if(bufTable[clockHand].loved)
	{
	    bufTable[clockHand].loved = false;
	    continue;
	}
	//skip if pinned
	if(bufTable[clockHand].pinCnt > 0)
	{
	    continue;
	}
        //write back if dirty
	if(bufTable[clockHand].dirty)
	{
            status = bufTable[clockHand].file->writePage(bufTable[clockHand].pageNo, &bufPool[clockHand]);
            if(status != OK)
	    	return status;
	    bufStats.diskwrites++;
        }
	
	break;

    }

    //Remove the previous buffer mapping
    bufMap.remove(bufTable[clockHand].file, bufTable[clockHand].pageNo);
					   
    //Get the buffer ready for a new requestor and return the
    //number of the buffer that was freed
    bufTable[clockHand].Clear();
    frame = clockHand;

    return status;
>>>>>>> .r34
}

	
const Status BufMgr::readPage(File* file, const int PageNo, Page*& page)
{
    unsigned int frameNo;
    
    //Check to see if the page is already in the buffer
    Status status = bufMap.lookup(file, PageNo, frameNo);
    
    //If not, free, then allocate a frame for the page
    if(status == BUFMAPNOTFOUND)
    {
    	status = allocBuf(frameNo);
        //Read the page from disk
	if(status == OK)
	{
	    status = file->readPage(PageNo, &bufPool[frameNo]);
	}
	if(status == OK)
	{
	    bufTable[frameNo].Set(file, PageNo);
    	    status = bufMap.insert(file, PageNo, frameNo);
	    bufStats.diskreads++;
	}
    }
    else
    {
    	//Increment pin cnt if page already in buffer
    	bufTable[frameNo].pinCnt++;
    	page = &bufPool[frameNo];
    }

    page = &bufPool[frameNo];
    bufStats.accesses++;
    return status;
}


const Status BufMgr::unPinPage(File* file, const int PageNo, const bool dirty, const bool love) 
{
    unsigned int frameNo;
    //Check to see if the page is already in the buffer
    Status status = bufMap.lookup(file, PageNo, frameNo);
    
    //Return error if not in the buffer mapping
    if(status == BUFMAPNOTFOUND)
    {
    	return status;
    }

    //Return error if the page is not pinned
    if(bufTable[frameNo].pinCnt == 0)
    {
    	return PAGENOTPINNED;
    }
    
    //Return errror if the page is not valid
    if(bufTable[frameNo].valid == false)
    {
    	return BADBUFFER;
    }

    //Decrement pin cnt and det dirty and loved bits
    bufTable[frameNo].pinCnt--;
    if(dirty)
    	bufTable[frameNo].dirty = dirty;
    if(love)
    	bufTable[frameNo].loved = love;

    return status;
}


const Status BufMgr::flushFile(File* file) 
{
    for (unsigned int i = 0; i < numBufs; i++)
    {
        if (bufTable[i].file == file && bufTable[i].pinCnt != 0)
            return PAGEPINNED;
    }
    for (unsigned int i = 0; i < numBufs; i++)
    {
        if (bufTable[i].file == file && bufTable[i].valid)
        {
            if (bufTable[i].dirty)
            {
                Status error = bufTable[i].file->writePage(bufTable[i].pageNo, &bufPool[i]);
                if (error != OK) return error;
                bufStats.diskwrites++;
            }
            bufMap.remove(file, bufTable[i].pageNo);
            bufTable[i].Clear();
        }
    }
    return OK;
}


const Status BufMgr::disposePage(File* file, const int pageNo)
{
    Status status;
    unsigned int frameNo;

    if(pageNo == 0)
    	return BADPAGENO;

    status = bufMap.lookup(file, pageNo, frameNo);

    //if page is in buffer, clear the table entry and remove from map
    if(status == OK)
    {
    	if(bufTable[frameNo].pinCnt > 0)
	    return PAGEPINNED;
	if(bufTable[frameNo].valid == false)
	    return BADBUFFER;

	bufTable[frameNo].Clear();
    	bufMap.remove(file, pageNo);
    }

    status = file->disposePage(pageNo);

    if(status != OK)
    	return status;

    bufStats.diskwrites++;

    return status;

}


const Status BufMgr::allocPage(File* file, int& pageNo, Page*& page) 
{
    Status status;
    unsigned int frameNo;
    
    bufStats.accesses++;
    
    //Allocate buffer for page
    status = allocBuf(frameNo);

    //If BUFFEREXCEEDED OR UNIXERR
    if(status != OK)
    	return status;
    
    status = file->allocatePage(pageNo);

    //If UNIXERR
    if(status != OK)
    	return status;

    bufStats.diskwrites++;

    //Update buffer table and buffer map
    bufTable[frameNo].Set(file, pageNo);
    status = bufMap.insert(file, pageNo, frameNo);

    //If BUFMAPERROR
    if(status != OK)
    	return status;

    page = & bufPool[frameNo];

    return status;
	
}



// return the number of pages in the buffer pool that are not pinned
int BufMgr::numUnpinnedPages()
{
  int numFree = 0;
  BufDesc* tmpbuf;

  for (unsigned i=0; i<numBufs; i++) 
  {
    tmpbuf = &(bufTable[i]);
    if (tmpbuf->pinCnt == 0) numFree++;
  }

  return numFree;
}

void BufMgr::printSelf(void) {

  BufDesc* tmpbuf;
  
  cout << endl << "Print buffer...\n";
  for (unsigned i=0; i<numBufs; i++) {
    tmpbuf = &(bufTable[i]);
    cout << i << "\t" << (char*)(&bufPool[i]) 
      << "\tpinCnt: " << tmpbuf->pinCnt;
    
    if (tmpbuf->valid == true)
      cout << "\tvalid\n";
    cout << endl;
  };
}
